<?php

namespace App\Http\Controllers\Api;

use App\Enums\AssetSourceType;
use App\Enums\AssetStatus;
use App\Exceptions\ApiException;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\FrontendUser;
use App\Models\Product;
use App\Models\ProductConfig;
use App\Services\WalletService;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class AssetGiveController extends Controller
{
    public function assetGiveList(): array
    {
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;
        $type = request()->type;
        $query = Asset::query()
            ->where('user_id', auth()->id())
            ->when('receive' === $type, function ($q) {
                return $q->where('source_type', AssetSourceType::GIVE);
            })
            ->when('give' === $type, function ($q) {
                return $q->where('asset_status', AssetStatus::GIVE);
            })
            ->when(!request()->has('type'), function ($q) {
                return $q->where(function (Builder $builder) {
                    $builder->where('source_type', AssetSourceType::GIVE)
                        ->orWhere('asset_status', AssetStatus::GIVE);
                });
            });

        $count = $query->count();
        $list = $query
            ->with(['product:contract_address,name,logo_url'])
            ->orderByDesc('updated_at')
            ->offset(($page - 1) * $limit)->limit($limit)
            ->get()->map(function (Asset $asset) use ($type) {
                $giveTime = '';
                if (AssetSourceType::GIVE == $asset->source_type) {
                    $giveTime = $asset->created_at->toDateTimeString();
                }
                if (AssetStatus::GIVE == $asset->asset_status) {
                    $giveTime = $asset->updated_at->toDateTimeString();
                }
                if (!$type) {
                    $type = $asset->asset_status->is(AssetStatus::GIVE) ? 'give' : 'receive';
                }

                return [
                    'id' => $asset->id,
                    'product_name' => $asset->product->name,
                    'product_logo_url' => $asset->product->logo_url,
                    'cert_sn' => $asset->cert_sn,
                    'give_time' => $giveTime,
                    'type' => $type,
                ];
            });

        return [
            'count' => $count,
            'list' => $list,
        ];
    }

    /** 资产转赠.
     *
     * @throws ApiException
     */
    public function assetGiveWithSecondPass()
    {
        request()->validate([
            'asset_id' => 'required',
            'receive_mobile' => 'required',
            'second_pass' => 'required',
        ]);

        /** @var FrontendUser $user */
        $user = auth()->user();
        if (!\Hash::check(request()->second_pass, $user->second_pass)) {
            throw new ApiException('二级密码不正确');
        }

        /** @var FrontendUser $receiveUser */
        $receiveUser = FrontendUser::query()
            ->where('mobile', request()->receive_mobile)
            ->where('agent_id', $user->agent_id)
            ->first();
        if (!$receiveUser) {
            throw new ApiException('接收手机号未注册');
        }
        if ($user->mobile == $receiveUser->mobile) {
            throw new ApiException('不能转赠给自己');
        }

        $this->doAssetGive($user, $receiveUser, request()->asset_id, );
    }

    /**
     * @param mixed $receiveUser
     * @param mixed $assetId
     *
     * @throws ApiException
     */
    private function doAssetGive(FrontendUser $user, $receiveUser, $assetId)
    {
        $lock = \Cache::lock('asset_give_' . $assetId, 5);

        /** @var Asset $asset */
        $asset = Asset::query()
            ->where('user_id', $user->id)
            ->whereIn('asset_status', [AssetStatus::HOLD])
            ->find($assetId);
        if (!$asset) {
            throw new ApiException('资产未找到或不可转赠');
        }
        /** @var Product $product */
        $product = Product::query()->where('contract_address', $asset->contract_address)->first();
        // 检查转赠配置
        $this->checkGiveConfig($asset, $product);

        try {
            $lock->block(1); // 如果获取不到锁，则等待2秒

            DB::transaction(function () use ($asset, $receiveUser, $user) {
                /** @var WalletService $walletService */
                $walletService = app()->get(WalletService::class);

                $transResult = $walletService->transfer(
                    $user->mobile,
                    $asset->contract_address,
                    [$asset->token_id],
                    $receiveUser->wallet_address
                );
                $transHash = Arr::get($transResult, $asset->token_id);
                if (!$transHash) {
                    throw new ApiException('转账返回值错误');
                }

                $toAsset = new Asset();
                $toAsset->user_id = $receiveUser->id;
                $toAsset->source_type = AssetSourceType::GIVE;
                $toAsset->source_id = $asset->id;
                $toAsset->contract_address = $asset->contract_address;
                $toAsset->token_id = $asset->token_id;
                $toAsset->trans_hash = $transHash ?? '';
                $toAsset->asset_status = AssetStatus::HOLD;
                $toAsset->save();

                $asset->asset_status = AssetStatus::GIVE;
                $asset->save();
            });
        } catch (LockTimeoutException $e) {
            throw new ApiException('请不要重复请求');
        } finally {
            $lock->release();
        }
    }

    /** 检查转赠配置.
     *
     * @throws ApiException
     */
    private function checkGiveConfig(Asset $asset, Product $product)
    {
        // 判断商品转赠配置
        $giveConfigs = $product->configs()
            ->whereIn(
                'config_key',
                [ProductConfig::GIVE_FORBID, ProductConfig::GIVE_DURATION, ProductConfig::IS_LOCKED]
            )
            ->get()
            ->mapWithKeys(function (ProductConfig $config) {
                return [$config->config_key => $config->config_value];
            });

        $giveForbid = (bool) Arr::get($giveConfigs, ProductConfig::GIVE_FORBID, true);
        $giveDuration = Arr::get($giveConfigs, ProductConfig::GIVE_DURATION, 0);
        $isLocked = (bool) Arr::get($giveConfigs, ProductConfig::IS_LOCKED, false);

        if ($isLocked) {
            throw new ApiException('资产已锁定，不可转赠');
        }
        if ($giveForbid) {
            throw new ApiException('该资产不支持转赠');
        }
        if ($giveDuration > 0 && now()->lt($asset->created_at->addDays($giveDuration))) {
            throw new ApiException("资产持有超过{$giveDuration}天后才可以转赠");
        }
    }
}
